#!/bin/bash

# 节点 ip
NODE_IP=192.168.30.121
# 容器运行时,可选择：docker, containerd
CONTAINER_RUNTIME=containerd
# k8s 集群名称
# K8S_NAME=cluster.local
# k8s 负载均衡器 ip
K8S_LB_IP=192.168.30.120
# k8s 负载均衡器 domain
K8S_LB_DOMAIN=lb.kubesphere.local
# k8s kubernetes svc ip
K8S_SVC_IP=10.233.0.1
# k8s 证书过期时间,单位天
K8S_CERT_EXPIRE=36500
# 获取当前时间，格式为年月日时分秒
CURRENT_TIME=$(date +"%Y%m%d%H%M%S")

echo "创建 k8s 相关文件存放目录"
mkdir -p /root/k8s-backup
echo "备份 k8s 相关文件"
cp -r /etc/kubernetes /root/k8s-backup/$CURRENT_TIME/
cp -r /var/lib/kubelet /root/k8s-backup/$CURRENT_TIME/


echo "创建k8s证书存放目录"
mkdir -p /root/new-certs

echo "切换到k8s证书存放目录"
cd /root/new-certs
rm -rf *

echo "生成ca.key"
openssl genpkey -algorithm RSA -out ca.key -pkeyopt rsa_keygen_bits:2048

echo "生成ca 证书"
openssl req -x509 -new -key ca.key -out ca.crt -days $K8S_CERT_EXPIRE \
    -subj "/CN=kubernetes" \
    -extensions v3_req \
    -config <(cat << EOF
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn

[dn]
CN = kubernetes

[v3_req]
basicConstraints = critical,CA:true
keyUsage = critical,digitalSignature,keyEncipherment,keyCertSign
subjectKeyIdentifier = hash
subjectAltName = DNS:kubernetes
EOF
)

echo "ca.crt, ca.key 生成完毕"
echo "-------------------------------------------------------------------------------------"

echo "生成front-proxy-ca.key"
openssl genpkey -algorithm RSA -out front-proxy-ca.key -pkeyopt rsa_keygen_bits:2048

echo "生成 front-proxy-ca 自签名证书"
openssl req -x509 -new -key front-proxy-ca.key -out front-proxy-ca.crt -days $K8S_CERT_EXPIRE \
    -subj "/CN=front-proxy-ca" \
    -extensions v3_req \
    -config <(cat << EOF
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn

[dn]
CN = front-proxy-ca

[v3_req]
subjectKeyIdentifier = hash
basicConstraints = critical, CA:TRUE
keyUsage = critical, digitalSignature, keyEncipherment, keyCertSign
subjectAltName = DNS:front-proxy-ca
EOF
)

echo "front-proxy-ca.crt, front-proxy-ca.key 生成完毕"
echo "-------------------------------------------------------------------------------------"

cat <<EOF > apiserver.conf 
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name

[req_distinguished_name]

[ v3_req ]
basicConstraints = critical, CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
authorityKeyIdentifier = keyid,issuer

[alt_names]
DNS.0 = localhost
DNS.1 = $HOSTNAME
DNS.2 = $HOSTNAME.cluster.local
DNS.3 = kubernetes
DNS.4 = kubernetes.default
DNS.5 = kubernetes.default.svc
DNS.6 = kubernetes.default.svc.cluster.local
DNS.7 = $K8S_LB_DOMAIN

IP.0 = 127.0.0.1
IP.1 = $NODE_IP
IP.2 = $K8S_LB_IP
IP.3 = $K8S_SVC_IP
EOF

echo "生成apiserver证书"
openssl req -newkey rsa:2048 -nodes -keyout apiserver.key -out apiserver.csr -subj "/CN=kube-apiserver" > /dev/null 2>&1
openssl x509 -req -in apiserver.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out apiserver.crt -days $K8S_CERT_EXPIRE -extensions v3_req -extfile apiserver.conf > /dev/null 2>&1

echo "apiserver.crt, apiserver.key 生成完毕"
echo "-------------------------------------------------------------------------------------"

echo "生成apiserver-kubelet-client证书"
openssl req -newkey rsa:2048 -nodes -keyout apiserver-kubelet-client.key -out apiserver-kubelet-client.csr -subj "/O=system:masters/CN=kube-apiserver-kubelet-client" > /dev/null 2>&1
openssl x509 -req -in apiserver-kubelet-client.csr \
    -CA ca.crt -CAkey ca.key \
    -CAcreateserial -out apiserver-kubelet-client.crt \
    -days $K8S_CERT_EXPIRE \
    -extfile <(echo "keyUsage = critical, digitalSignature, keyEncipherment"; echo "extendedKeyUsage = clientAuth"; echo "basicConstraints = critical, CA:FALSE"; echo "authorityKeyIdentifier = keyid,issuer")

echo "apiserver-kubelet-client.crt, apiserver-kubelet-client.key 生成完毕"
echo "-------------------------------------------------------------------------------------"

echo "生成front-proxy-client证书"
openssl req -newkey rsa:2048 -nodes -keyout front-proxy-client.key -out front-proxy-client.csr -subj "/CN=front-proxy-client" > /dev/null 2>&1
openssl x509 -req \
    -in front-proxy-client.csr \
    -CA front-proxy-ca.crt \
    -CAkey front-proxy-ca.key \
    -CAcreateserial \
    -out front-proxy-client.crt \
    -days $K8S_CERT_EXPIRE \
    -extfile <(echo "keyUsage = critical, digitalSignature, keyEncipherment"; echo "extendedKeyUsage = clientAuth"; echo "basicConstraints = critical, CA:FALSE"; echo "authorityKeyIdentifier = keyid,issuer")

echo "front-proxy-client.crt, front-proxy-client.key 生成完毕"
echo "-------------------------------------------------------------------------------------"

echo "更换新的证书"
mv /etc/kubernetes/pki /etc/kubernetes/$CURRENT_TIME-pki.bak
cp -r /root/new-certs /etc/kubernetes/pki

echo "生成新的 sa.key 和 sa.pub 文件"
cd /etc/kubernetes/pki
kubeadm init phase certs sa

echo "备份 admin.conf 文件"
mkdir -p /etc/kubernetes/$CURRENT_TIME-conf.bak
cd /etc/kubernetes/
mv admin.conf controller-manager.conf scheduler.conf kubelet.conf /etc/kubernetes/$CURRENT_TIME-conf.bak/
rm -rf /var/lib/kubelet/pki/kubelet*

echo "生成新的 admin.conf, controller-manager.conf, scheduler.conf, kubelet.conf文件"
kubeadm init phase kubeconfig admin
kubeadm init phase kubeconfig controller-manager
kubeadm init phase kubeconfig scheduler
kubeadm kubeconfig user --org system:nodes --client-name system:node:$HOSTNAME > /etc/kubernetes/kubelet.conf

echo "修改 admin.conf 和 kubelet.conf 文件中的负载均衡器地址"
sed -i "s#server: https://$NODE_IP:6443#server: https://$K8S_LB_DOMAIN:6443#g" admin.conf kubelet.conf

# echo "修改 admin.conf, controller-manager.conf, scheduler.conf, kubelet.conf 文件中的集群名称"
# sed -i -r "s/kubernetes$\b/$K8S_NAME/g" admin.conf controller-manager.conf scheduler.conf kubelet.conf

echo "重新生成 kubectl 访问集群权限"
cp -f /etc/kubernetes/admin.conf $HOME/.kube/config

echo "重启kubelet和containerd,让证书生效,并重启组件"
if [ "$CONTAINER_RUNTIME" == "docker" ]; then
    systemctl daemon-reload && systemctl restart docker && systemctl restart kubelet.service
    docker ps |grep -E 'kube-apiserver|kube-controller-manager|kube-scheduler|etcd' | awk -F ' ' '{print $1}' |xargs docker stop
    docker ps -a -q -f status=exited | xargs docker rm
    echo "删除 kube-vip 容器"
    docker ps -a | grep kube-vip | awk '{print $1}' | xargs -I {} docker rm -f {}
else
    systemctl daemon-reload && systemctl restart containerd.service && systemctl restart kubelet.service 
    crictl ps |grep -E 'kube-apiserver|kube-controller-manager|kube-scheduler|etcd' | awk -F ' ' '{print $1}' |xargs crictl stop
    crictl ps -a -q -s Exited | xargs crictl rm
    echo "删除 kube-vip 容器"
    crictl ps -a | grep kube-vip | awk '{print $1}' | xargs -I {} crictl rm -f {}
fi

sleep 10
echo "更新 calico-node, calico-kube-controllers, kube-proxy, coredns, nodelocaldns组件"
kubectl -n kube-system delete pods -l k8s-app=calico-node
kubectl -n kube-system delete pods -l k8s-app=calico-kube-controllers
kubectl -n kube-system delete pods -l k8s-app=kube-proxy
kubectl -n kube-system delete pods -l k8s-app=kube-dns
kubectl -n kube-system delete pods -l k8s-app=nodelocaldns


echo "验证集群"
kubectl get nodes -o wide
kubectl get pods -A